package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.core.Promise


////////////////////
// Downloads
////////////////////
/**
 * Use the `Browser.downloads` API to programmatically initiate, monitor, manipulate, and search for downloads.
 *
 * Permissions: "downloads"
 */
interface Downloads {
    // Prompt the user to accept a dangerous download.
    // Can only be called from a visible context (tab, window, or page/browser action popup).
    // Does not automatically accept dangerous downloads.
    // If the download is accepted, then an onChanged event will fire, otherwise nothing will happen.
    // When all the data is fetched into a temporary file and either the download is not dangerous or the danger has been accepted,
    // then the temporary file is renamed to the target filename, the state changes to 'complete', and onChanged fires.
    @PromiseStyleMinVersion(96)
     fun acceptDanger(
        // The identifier for the DownloadItem.
        downloadId: Number,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Cancel a download. When callback is run, the download is cancelled, completed, interrupted or doesn't exist anymore.
    @PromiseStyleMinVersion(96)
     fun cancel(
        downloadId: Number,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Download a URL. If the URL uses the HTTP[S] protocol,
    // then the request will include all cookies currently set for its hostname.
    // If both filename and saveAs are specified, then the Save As dialog will be displayed,
    // pre-populated with the specified filename. If the download started successfully,
    // callback will be called with the new DownloadItem's downloadId.
    // If there was an error starting the download,
    // then callback will be called with downloadId=undefined and runtime.lastError will contain a descriptive string.
    // The error strings are not guaranteed to remain backwards compatible between releases.
    // Extensions must not parse it.
    @PromiseStyleMinVersion(96)
     fun download(
        // What to download and how.
        options: DownloadOptions,
        callback: Value1Function<Number>? = null
    ): Promise<Number>

    // Erase matching DownloadItem from history without deleting the downloaded file.
    // An onErased event will fire for each DownloadItem that matches query, then callback will be called.
    // @return {callback.erasedIds}
    @PromiseStyleMinVersion(96)
     fun erase(
        query: DownloadQuery,
        callback: Value1Function<List<Number>>? = null
    ): Promise<List<Number>>

    // Retrieve an icon for the specified download. For new downloads,
    // file icons are available after the onCreated event has been received.
    // The image returned by this function while a download is in progress may be different from the image returned after the download is complete.
    // Icon retrieval is done by querying the underlying operating system or toolkit depending on the platform.
    // The icon that is returned will therefore depend on a number of factors including state of the download,
    // platform, registered file types and visual theme.
    // If a file icon cannot be determined, runtime.lastError will contain an error message.
    // @return {callback.iconURL}
    @PromiseStyleMinVersion(96)
     fun getFileIcon(
        // The identifier for the download.
        downloadId: Number,
        options: GetFileIconOptions? = null,
        callback: Value1Function<String>? = null
    ): Promise<String?>

    // Opens the downloaded file now if the DownloadItem is complete;
    // otherwise returns an error through runtime.lastError.
    // This method requires the "downloads.open" permission in addition to the "downloads" permission.
    // An onChanged event fires when the item is opened for the first time.
    // This method can only be called in response to a user gesture.
    @PromiseStyleMinVersion(123)
     fun open(
        // The identifier for the downloaded file.
        downloadId: Number,
        @ChromeMinVersion(123)
        callback: VoidFunction? = null
    ): Promise<Void>

    // Pause the download. If the request was successful the download is in a paused state.
    // Otherwise runtime.lastError contains an error message.
    // The request will fail if the download is not active.
    @PromiseStyleMinVersion(96)
     fun pause(
        // The id of the download to pause.
        downloadId: Number,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Remove the downloaded file if it exists and the DownloadItem is complete;
    // otherwise return an error through runtime.lastError.
    @PromiseStyleMinVersion(96)
     fun removeFile(
        downloadId: Number,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Resume a paused download. If the request was successful the download is in progress and unpaused.
    // Otherwise runtime.lastError contains an error message.
    // The request will fail if the download is not active.
    @PromiseStyleMinVersion(96)
     fun resume(
        // The id of the download to resume.
        downloadId: Number,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Find DownloadItem. Set query to the empty object to get all DownloadItem.
    // To get a specific DownloadItem, set only the id field.
    // To page through a large number of items, set orderBy: ['-startTime'],
    // set limit to the number of items per page, and set startedAfter to the startTime of the last item from the last page.
    @PromiseStyleMinVersion(96)
     fun search(
        query: DownloadQuery,
        callback: Value1Function<List<DownloadItem>>? = null
    ): Promise<List<DownloadItem>>

    // Enable or disable the gray shelf at the bottom of every window associated with the current browser profile.
    // The shelf will be disabled as long as at least one extension has disabled it.
    // Enabling the shelf while at least one other extension has disabled it will return an error through runtime.lastError.
    // Requires the "downloads.shelf" permission in addition to the "downloads" permission.
    @Deprecated(
        "Deprecated since Chrome 117",
        replaceWith = ReplaceWith("Use setUiOptions instead.")
    )
     fun setShelfEnabled(
        enabled: Boolean
    )

    // Change the download UI of every window associated with the current browser profile.
    // As long as at least one extension has set UiOptions.enabled to false, the download UI will be hidden.
    // Setting UiOptions.enabled to true while at least one other extension has disabled it will return an error through runtime.lastError.
    // Requires the "downloads.ui" permission in addition to the "downloads" permission.
    @ChromeMinVersion(105)
     fun setUiOptions(
        // Encapsulate a change to the download UI.
        options: UiOptions,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Show the downloaded file in its folder in a file manager.
     fun show(
        // The identifier for the downloaded file.
        downloadId: Number
    )

    // Show the default Downloads folder in a file manager.
     fun showDefaultFolder()

    // When any of a DownloadItem's properties except bytesReceived and estimatedEndTime changes,
    // this event fires with the downloadId and an object containing the properties that changed.
     val onChanged: Events.Event<Value1Function<DownloadDelta>>

    // This event fires with the DownloadItem object when a download begins.
     val onCreated: Events.Event<Value1Function<DownloadItem>>

    // During the filename determination process, extensions will be given the opportunity to override the target DownloadItem.filename.
    // Each extension may not register more than one listener for this event.
    // Each listener must call suggest exactly once, either synchronously or asynchronously.
    // If the listener calls suggest asynchronously, then it must return true.
    // If the listener neither calls suggest synchronously nor returns true, then suggest will be called automatically.
    // The DownloadItem will not complete until all listeners have called suggest.
    // Listeners may call suggest without any arguments in order to allow the download to use downloadItem.filename for its filename,
    // or pass a suggestion object to suggest in order to override the target filename.
    // If more than one extension overrides the filename, then the last extension installed whose listener passes a suggestion object to suggest wins.
    // In order to avoid confusion regarding which extension will win, users should not install extensions that may conflict.
    // If the download is initiated by download and the target filename is known before the MIME type and tentative filename have been determined,
    // pass filename to download instead.
     val onDeterminingFilename: Events.Event<Value2Function<DownloadItem, Value1Function<FilenameSuggestion>?>>

    // Fires with the downloadId when a download is erased from history.
     val onErased: Events.Event<Value1Function<Number>>

    data class BooleanDelta(
        var current: Boolean?,
        var previous: Boolean?
    )

    enum class DangerType {
        // The download's filename is suspicious.
        file,

        // The download's URL is known to be malicious.
        url,

        // The downloaded file is known to be malicious.
        content,

        // The download's URL is not commonly downloaded and could be dangerous.
        uncommon,

        // The download came from a host known to distribute malicious binaries and is likely dangerous.
        host,

        // The download is potentially unwanted or unsafe. E.g. it could make changes to browser or computer settings.
        unwanted,

        // The download presents no known danger to the user's computer.
        safe,

        // The user has accepted the dangerous download.
        accepted,
    }

    data class DoubleDelta(
        var current: Double?,
        var previous: Double?
    )

    data class StringDelta(
        var current: String?,
        var previous: String?
    )

    data class DownloadDelta(
        // The change in canResume, if any.
        var canResume: BooleanDelta?,
        // The change in danger, if any.
        var danger: StringDelta?,
        // The change in endTime, if any.
        var endTime: StringDelta?,
        // The change in error, if any.
        var error: StringDelta?,
        // The change in exists, if any.
        var exists: BooleanDelta?,
        // The change in fileSize, if any.
        var fileSize: DoubleDelta?,
        // The change in filename, if any.
        var filename: StringDelta?,
        // The change in finalUrl, if any.
        @ChromeMinVersion(54)
        var finalUrl: StringDelta?,
        // The id of the DownloadItem that changed.
        var id: Number,
        // The change in mime, if any.
        var mime: StringDelta?,
        // The change in paused, if any.
        var paused: BooleanDelta?,
        // The change in startTime, if any.
        var startTime: StringDelta?,
        // The change in state, if any.
        var state: StringDelta?,
        // The change in totalBytes, if any.
        var totalBytes: DoubleDelta?,
        // The change in url, if any.
        var url: StringDelta?
    )

    data class DownloadItem(
        // The identifier for the extension that initiated this download if this download was initiated by an extension.
        // Does not change once it is set.
        var byExtensionId: String?,
        // The localized name of the extension that initiated this download if this download was initiated by an extension.
        // May change if the extension changes its name or if the user changes their locale.
        var byExtensionName: String?,
        // Number of bytes received so far from the host, without considering file compression.
        var bytesReceived: Number,
        // True if the download is in progress and paused, or else if it is interrupted and can be resumed starting from where it was interrupted.
        var canResume: Boolean,
        // Indication of whether this download is thought to be safe or known to be suspicious.
        var danger: DangerType,
        // The time when the download ended in ISO 8601 format. May be passed directly to the Date constructor:
        // chrome.downloads.search({}, function(items){items.forEach(function(item){if (item.endTime) console.log(new Date(item.endTime))})})
        var endTime: String?,
        // Why the download was interrupted. Several kinds of HTTP errors may be grouped under one of the errors beginning with SERVER_.
        // Errors relating to the network begin with NETWORK_, errors relating to the process of writing the file to the file system begin with FILE_,
        // and interruptions initiated by the user begin with USER_.
        var error: InterruptReason?,
        // Estimated time when the download will complete in ISO 8601 format. May be passed directly to the Date constructor:
        // chrome.downloads.search({}, function(items){items.forEach(function(item){if (item.estimatedEndTime) console.log(new Date(item.estimatedEndTime))})})
        var estimatedEndTime: String?,
        // Whether the downloaded file still exists. This information may be out of date because Chrome does not automatically watch for file removal.
        // Call search() in order to trigger the check for file existence. When the existence check completes,
        // if the file has been deleted, then an onChanged event will fire.
        // Note that search() does not wait for the existence check to finish before returning,
        // so results from search() may not accurately reflect the file system. Also,
        // search() may be called as often as necessary, but will not check for file existence any more frequently than once every 10 seconds.
        var exists: Boolean,
        // Number of bytes in the whole file post-decompression, or -1 if unknown.
        var fileSize: Number,
        // Absolute local path.
        var filename: String,
        // The absolute URL that this download is being made from, after all redirects.
        @ChromeMinVersion(54)
        var finalUrl: String,
        // An identifier that is persistent across browser sessions.
        var id: Number,
        // False if this download is recorded in the history, true if it is not recorded.
        var incognito: Boolean,
        // The file's MIME type.
        var mime: String,
        // True if the download has stopped reading data from the host, but kept the connection open.
        var pasued: Boolean,
        // Absolute URL.
        var referrer: String,
        // The time when the download began in ISO 8601 format. May be passed directly to the Date constructor:
        // chrome.downloads.search({}, function(items){items.forEach(function(item){console.log(new Date(item.startTime))})})
        var startTime: String,
        // Indicates whether the download is progressing, interrupted, or complete.
        var state: State,
        // Number of bytes in the whole file, without considering file compression, or -1 if unknown.
        var totalBytes: Number,
        // The absolute URL that this download initiated from, before any redirects.
        var url: String
    )

    data class DownloadOptions(
        // Post body.
        var body: String?,
        // The action to take if filename already exists.
        var conflictAction: FilenameConflictAction?,
        // A file path relative to the Downloads directory to contain the downloaded file,
        // possibly containing subdirectories. Absolute paths, empty paths,
        // and paths containing back-references ".." will cause an error.
        // onDeterminingFilename allows suggesting a filename after the file's MIME type and a tentative filename have been determined.
        var filename: String?,
        // Extra HTTP headers to send with the request if the URL uses the HTTP[s] protocol.
        // Each header is represented as a dictionary containing the keys name and either value or binaryValue,
        // restricted to those allowed by XMLHttpRequest.
        var headers: List<HeaderNameValuePair>?,
        // The HTTP method to use if the URL uses the HTTP[S] protocol.
        var method: HttpMethod?,
        // Use a file-chooser to allow the user to select a filename regardless of whether filename is set or already exists.
        var saveAs: Boolean?,
        // The URL to download.
        var url: String,
    )

    data class DownloadQuery(
        // Number of bytes received so far from the host, without considering file compression.
        var bytesReceived: Number?,
        // Indication of whether this download is thought to be safe or known to be suspicious.
        var danger: DangerType?,
        // The time when the download ended in ISO 8601 format.
        var endTime: String?,
        // Limits results to DownloadItem that ended after the given ms in ISO 8601 format
        var endedAfter: String?,
        // Limits results to DownloadItem that ended before the given ms in ISO 8601 format.
        var endedBefore: String?,
        // Why a download was interrupted.
        var error: InterruptReason?,
        // Whether the downloaded file exists;
        var exists: Boolean?,
        // Number of bytes in the whole file post-decompression, or -1 if unknown.
        var fileSize: Number?,
        // Absolute local path.
        var filename: String?,
        // Limits results to DownloadItem whose filename matches the given regular expression.
        var filenameRegex: String?,
        // The absolute URL that this download is being made from, after all redirects.
        @ChromeMinVersion(54)
        var finalUrl: String?,
        // Limits results to DownloadItem whose finalUrl matches the given regular expression.
        @ChromeMinVersion(54)
        var finalUrlRegex: String?,
        // The id of the DownloadItem to query.
        var id: Number?,
        // The maximum number of matching DownloadItem returned. Defaults to 1000.
        // Set to 0 in order to return all matching DownloadItem.
        // See search for how to page through results.
        var limit: Number?,
        // The file's MIME type.
        var mime: String?,
        // Set elements of this array to DownloadItem properties in order to sort search results.
        // For example, setting orderBy=['startTime'] sorts the DownloadItem by their start time in ascending order.
        // To specify descending order, prefix with a hyphen: '-startTime'.
        var orderBy: List<String>?,
        // True if the download has stopped reading data from the host, but kept the connection open.
        var paused: Boolean?,
        // This array of search terms limits results to DownloadItem whose filename or url or finalUrl
        // contain all of the search terms that do not begin with a dash '-' and none of the search terms that do begin with a dash.
        var query: List<String>?,
        // The time when the download began in ISO 8601 format.
        var startTime: String?,
        // Limits results to DownloadItem that started after the given ms in ISO 8601 format.
        var startedAfter: String?,
        // Limits results to DownloadItem that started before the given ms in ISO 8601 format.
        var startedBefore: String?,
        // Indicates whether the download is progressing, interrupted, or complete.
        var state: State?,
        // Number of bytes in the whole file, without considering file compression, or -1 if unknown.
        var totalBytes: Number?,
        // Limits results to DownloadItem whose totalBytes is greater than the given integer.
        var totalBytesGreater: Number?,
        // Limits results to DownloadItem whose totalBytes is less than the given integer.
        var totalBytesLess: Number?,
        // The absolute URL that this download initiated from, before any redirects.
        var url: String?,
        // Limits results to DownloadItem whose url matches the given regular expression.
        var urlRegex: String?,
    )

    enum class State {
        // The download is currently receiving data from the server.
        in_progress,

        // An error broke the connection with the file host.
        interrupted,

        // The download completed successfully.
        complete
    }

    enum class InterruptReason {
        FILE_FAILED,
        FILE_ACCESS_DENIED,
        FILE_NO_SPACE,
        FILE_NAME_TOO_LONG,
        FILE_TOO_LARGE,
        FILE_VIRUS_INFECTED,
        FILE_TRANSIENT_ERROR,
        FILE_BLOCKED,
        FILE_SECURITY_CHECK_FAILED,
        FILE_TOO_SHORT,
        FILE_HASH_MISMATCH,
        FILE_SAME_AS_SOURCE,
        NETWORK_FAILED,
        NETWORK_TIMEOUT,
        NETWORK_DISCONNECTED,
        NETWORK_SERVER_DOWN,
        NETWORK_INVALID_REQUEST,
        SERVER_FAILED,
        SERVER_NO_RANGE,
        SERVER_BAD_CONTENT,
        SERVER_UNAUTHORIZED,
        SERVER_CERT_PROBLEM,
        SERVER_FORBIDDEN,
        SERVER_UNREACHABLE,
        SERVER_CONTENT_LENGTH_MISMATCH,
        SERVER_CROSS_ORIGIN_REDIRECT,
        USER_CANCELED,
        USER_SHUTDOWN,
        CRASH,
    }

    enum class FilenameConflictAction {
        // To avoid duplication, the filename is changed to include a counter before the filename extension.
        uniquify,

        // he existing file will be overwritten with the new file.
        overwrite,

        // The user will be prompted with a file chooser dialog.
        prompt,
    }

    data class FilenameSuggestion(
        // The action to take if filename already exists.
        var conflictAction: FilenameConflictAction?,
        // The DownloadItem's new target DownloadItem.filename, as a path relative to the user's default Downloads directory,
        // possibly containing subdirectories. Absolute paths, empty paths, and paths containing back-references ".." will be ignored.
        // filename is ignored if there are any onDeterminingFilename listeners registered by any extensions.
        var filename: String
    )

    data class GetFileIconOptions(
        // The size of the returned icon. The icon will be square with dimensions size * size pixels.
        // The default and largest size for the icon is 32x32 pixels. The only supported sizes are 16 and 32.
        // It is an error to specify any other size.
        var size: Number?,
    )

    data class HeaderNameValuePair(
        // Name of the HTTP header.
        var name: String,
        // Value of the HTTP header.
        var value: String,
    )

    enum class HttpMethod {
        GET,
        POST
    }

    @ChromeMinVersion(105)
    data class UiOptions(
        // Enable or disable the download UI.
        var enabled: Boolean,
    )

}